﻿//-----------------------------------------------------------------------
// <copyright file="Service.cs" company="Justin Wallace Saunders">
//     Copyright (c) Microsoft Public License. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------
namespace Community.Tools.Windows.Service
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.IO;
    using System.ServiceProcess;
    using System.Threading;
    using Community.Tools.Plugins;
    using log4net;

    /// <summary>
    /// Service class.
    /// </summary>
    public partial class Service : ServiceBase
    {
        /// <summary>
        /// Log Writer.
        /// </summary>
        private static readonly ILog Log = LogManager.GetLogger(typeof(Program));

        /// <summary>
        /// Initializes a new instance of the <see cref="Service"/> class.
        /// </summary>
        /// <param name="settings">Service settings</param>
        public Service(Settings settings = null)
        {
            this.Settings = settings == null ? new Settings() : settings;
            this.InitializeComponent();
           
            //// Event Log Initialization
            if (!EventLog.SourceExists(this.Settings.EventLogSourceName))
            {
                EventLog.CreateEventSource(this.Settings.EventLogSourceName, this.Settings.EventLogName);
            }

            this.eventLog.Source = this.Settings.EventLogSourceName;
            this.eventLog.Log = this.Settings.EventLogName;
            Log.Info("Event Log Name: " + this.Settings.EventLogName);
            Log.Info("Event Log Source Name: " + this.Settings.EventLogSourceName);
            Log.Info("Plugin Directory: " + this.Settings.PluginDirectory);
            Log.Info("Service Polling Interval: " + this.Settings.ServicePollingInterval);
            this.Plugins = new PluginManager();
            string path = this.Settings.PluginDirectory;
            if (!System.IO.Directory.Exists(path))
            {
                Log.Info(string.Format("Directory {0} not found. Plugins haven't been loaded!"));
            }
            else
            {
                try
                {
                    foreach (FileInfo f in new DirectoryInfo(path).GetFiles("*.dll"))
                    {
                        this.Plugins.LoadFile(f.FullName);
                    }

                    Log.Info("Plugins have loaded from plugin directory");
                }
                catch (Exception)
                {
                    Log.Info("Can't get access to the directory");
                }
            }

            if (this.Settings.TimetoLiveInterval > 0)
            {
                this.TimerStopCurrentService = new System.Timers.Timer(this.Settings.TimetoLiveInterval * 1000);
                this.TimerStopCurrentService.AutoReset = false;
                this.TimerStopCurrentService.Enabled = true;
                this.TimerStopCurrentService.Elapsed += new System.Timers.ElapsedEventHandler(this.TimerStopCurrentService_Elapsed);
            }
        }

        /// <summary>
        /// Gets or sets the service settings.
        /// </summary>
        private Settings Settings { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether the service should continue polling.
        /// </summary>
        private bool Stopped { get; set; }
        
        /// <summary>
        /// Gets or sets a value of plugins.
        /// </summary>
        private PluginManager Plugins { get; set; }

        /// <summary>
        /// Gets or sets the number of seconds the service should run.
        /// </summary>
        private System.Timers.Timer TimerStopCurrentService { get; set; }
       
        /// <summary>
        /// Service start method.
        /// </summary>
        /// <param name="args">A list of arguments</param>
        protected override void OnStart(string[] args)
        {
            this.Stopped = false;
            this.eventLog.WriteEntry("Service started");
            Log.Info("Wirestorm service started");
            
            try
            {
                this.fileSystemWatcher.Path = this.Settings.PluginDirectory;
            }
            catch (Exception)
            {
                Log.Info("Invalid Directory.");
            }
            
            //// Thread windowserviceprocess = new Thread(new ThreadStart(this.WindowService));
            Thread windowserviceprocess = new Thread(() => this.WindowService());

            windowserviceprocess.Start();
        }

         /// <summary>
        /// Window service stop method.
        /// </summary>
        protected override void OnStop()
        {
            this.Stopped = true;
            this.eventLog.WriteEntry("Service stopped");
            Log.Info("Service stopped");
        }

        /// <summary>
        /// Thread work
        /// </summary>
        private void WindowService()
        {
            Settings settings = new Settings();
            while (!this.Stopped)
            {
                string path = this.Settings.PluginDirectory;
                Thread.Sleep(TimeSpan.FromSeconds(this.Settings.ServicePollingInterval));
                foreach (FileInfo f in new DirectoryInfo(path).GetFiles("*.dll"))
                {
                    this.Execute(settings.Dictionary);
                }
            }
        }

        /// <summary>
        /// Execute function of plugin
        /// </summary>
        /// <param name="dictionary">Dictionary of command line argument</param>
        private void Execute(Dictionary<string, string> dictionary)
        {
            Log.Info("Plugin function Executed");
        }

        /// <summary>
        /// Loading the assembly from external directory
        /// </summary>
        /// <param name="filePath">File path information</param>
        private void TryLoadPluginAssembly(string filePath)
        {
            Log.Info("New dll file added");
            this.Plugins.LoadFile(filePath);
        }

        /// <summary>
        /// Event occurs when the contents of a File or Directory are changed
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">FileSystemEventArgs event recieve only *.dll type</param>
        private void FileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
        {
            ////Code here for newly changed file or directory
            this.Plugins.LoadFile(e.FullPath);
        }

        /// <summary>
        /// Event occurs when the a File or Directory is created
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">Filesystem event recieve only *.dll type </param>
        private void FileSystemWatcher_Created(object sender, FileSystemEventArgs e)
        {
            // TODO: Only fire the TryLoadPluginAssembly when the item is a file and a *.dll
            Log.Info("file created");
            this.TryLoadPluginAssembly(e.FullPath);
        }

        /// <summary>
        /// Event occurs when the a File or Directory is deleted
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">Filesystem event</param>
        private void FileSystemWatcher_Deleted(object sender, FileSystemEventArgs e)
        {
            // Code here for newly deleted file or directory
        }

        /// <summary>
        /// Event occurs when the a File or Directory is renamed
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">Filesystem event</param>
        private void FileSystemWatcher_Renamed(object sender, RenamedEventArgs e)
        {
            // Code here for newly renamed file or directory
        }

        /// <summary>
        /// Event occurs when the a time interval of window service completed.
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">Timer event</param>
        private void TimerStopCurrentService_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Log.Info("stopping current service");
            this.OnStop();
        }
     }
}
